package com.example.config;

import com.example.dao.Permission;
import com.example.dao.Role;
import com.example.security.auth.ExternalAuthenticationProvider;
import com.example.security.auth.rest.RestAuthenticationFailureHandler;
import com.example.security.auth.rest.RestAuthenticationFilter;
import com.example.security.auth.rest.RestAuthenticationSuccessHandler;
import com.example.security.decision.MyAccessDecisionManager;
import com.example.security.decision.MyInvocationSecurityMetadataSourceService;
import com.example.security.dsl.ClientErrorLoggingConfigurer;
import com.example.security.jwt.JwtFilter;
import com.example.security.userdetails.UserDetailsPasswordServiceImpl;
import com.example.security.userdetails.UserDetailsServiceImpl;
import com.example.service.PermissionService;
import com.example.service.RoleService;
import com.example.service.UserCacheService;
import com.example.util.JwtUtil;
import com.fasterxml.jackson.databind.ObjectMapper;

import lombok.RequiredArgsConstructor;
import lombok.val;
import org.springframework.boot.autoconfigure.security.servlet.PathRequest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.MessageDigestPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport;

import java.util.*;

@RequiredArgsConstructor
@EnableWebSecurity(debug = true)
@Configuration
@Order(99)
@Import(SecurityProblemSupport.class)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final SecurityProblemSupport problemSupport;
    private final JwtFilter jwtFilter;
    private final Environment environment;
    private final ObjectMapper objectMapper;
    private final UserDetailsServiceImpl userDetailsServiceImpl;
    private final UserDetailsPasswordServiceImpl userDetailsPasswordServiceImpl;
    private final UserCacheService userCacheService;
    private final JwtUtil jwtUtil;
    private final RoleService roleService;
    private final PermissionService permissionService;
//    private final Environment environment;
//    private final LdapProperties ldapProperties;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry httpSecurity
//         List<Role> roleList=roleService.findAllRole();
//        List<String>permissionList=null;
//        Map<Role,List<String>>rp=new HashMap<>();
//         for(Role role:roleList){
//           permissionList=  permissionService.findPermissionPatchStringByRole(role);
//             rp.put(role,permissionList);
//         }

        http
                .httpBasic(AbstractHttpConfigurer::disable)
                .csrf(AbstractHttpConfigurer::disable)
                .logout(AbstractHttpConfigurer::disable)
                .cors(cors -> cors.configurationSource(corsConfigurationSource())) // 配置跨域
                .sessionManagement(sessionManagement -> sessionManagement
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .exceptionHandling(exceptionHandling -> exceptionHandling
                        .authenticationEntryPoint(problemSupport)
                        .accessDeniedHandler(problemSupport))
                .authorizeRequests()
                .antMatchers("/", "/authorize/**")//允许访问
                .permitAll()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                                             @Override
                                             public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                                                 object.setSecurityMetadataSource(cfisms());
                                                 object.setAccessDecisionManager(cadm());
                                                 return object;
                                             }
                                         }
                )
                .anyRequest().authenticated()

//                .accessDecisionManager()
                .and()
//                .authorizeRequests((authorizeRequests) -> {
//                    ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry httpSecurity = authorizeRequests
//                            .mvcMatchers("/", "/authorize/**").permitAll();
//                    rp.forEach((key, value) -> {
//
//                        if(value.size()>0){
//                        String[] permissionUrls = value.toArray(new String[0]);
//                        httpSecurity.antMatchers(permissionUrls).hasRole(key.getAuthority().substring(5));
//                        }
//                        //httpSecurity.antMatchers(permissionUrls).access("hasRole('"+key.getAuthority()+"')");
//                    });
//                    //httpSecurity.antMatchers("/admin/**", "/user/**", "/role/**","/permission/**").hasRole("ADMIN");
//                    httpSecurity.anyRequest().denyAll();
//                    //httpSecurity.anyRequest().authenticated();
//                })
                // .addFilterBefore(new LDAPAuthorizationFilter(new AntPathRequestMatcher("/api/**")), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)

//            .addFilterAt(restAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
        ;
    }

    @Override
    public void configure(WebSecurity web) {
        web
                .ignoring()
                .antMatchers("/resources/**", "/static/**", "/public/**", "/h2-console/**")
                .requestMatchers(PathRequest.toStaticResources().atCommonLocations());
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        /*
         * 希望使用内置的 LDAP 认证可以使用下面代码块代替自定义的 LDAPAuthenticationProvider
         * auth.ldapAuthentication()
         *             .userDnPatterns("uid={0}")
         *             .contextSource()
         *             .url(ldapProperties.getUrls()[0])
         *             .root(ldapProperties.getBase());
         */
        auth
                .authenticationProvider(new ExternalAuthenticationProvider());

        auth
                .userDetailsService(userDetailsServiceImpl) // 配置 AuthenticationManager 使用 userService
                .passwordEncoder(passwordEncoder()) // 配置 AuthenticationManager 使用 userService
                .userDetailsPasswordManager(userDetailsPasswordServiceImpl); // 配置密码自动升级服务
    }

    /**
     * 我们在 Spring Boot 中有几种其他方式配置 CORS
     * 参见 https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-cors
     * Mvc 的配置方式见 WebMvcConfig 中的代码
     *
     * @return CorsConfigurationSource
     */
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        // 允许跨域访问的主机
//        if (environment.acceptsProfiles(Profiles.of("dev"))) {
//            configuration.setAllowedOrigins(Collections.singletonList("http://localhost:4001"));
//        } else {
//            configuration.setAllowedOrigins(Collections.singletonList("https://uaa.imooc.com"));
//        }
        configuration.setAllowedOrigins(Collections.singletonList("http://localhost:4001"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(Collections.singletonList("*"));
        //二次验证的时候，有X-Authenticate的响应头
        configuration.addExposedHeader("X-Authenticate");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    @Bean
    public ClientErrorLoggingConfigurer clientErrorLogging() {
        return new ClientErrorLoggingConfigurer(new ArrayList<>());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        // 默认编码算法的 Id
        val idForEncode = "bcrypt";
        // 要支持的多种编码器
        val encoders = Map.of(
                idForEncode, new BCryptPasswordEncoder(),
                "SHA-1", new MessageDigestPasswordEncoder("SHA-1")
        );
        return new DelegatingPasswordEncoder(idForEncode, encoders);
    }

    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    //    @Bean
//    public RestAuthenticationFilter restAuthenticationFilter() throws Exception {
//        val filter = new RestAuthenticationFilter(objectMapper, userDetailsServiceImpl, userCacheService);
//        filter.setAuthenticationSuccessHandler(new RestAuthenticationSuccessHandler(jwtUtil));
//        filter.setAuthenticationFailureHandler(new RestAuthenticationFailureHandler(objectMapper));
//        filter.setAuthenticationManager(authenticationManager());
//        filter.setFilterProcessesUrl("/authorize/login");
//        return filter;
//    }
    @Bean
    MyInvocationSecurityMetadataSourceService cfisms() {
        return new MyInvocationSecurityMetadataSourceService();
    }

    @Bean
    MyAccessDecisionManager cadm() {
        return new MyAccessDecisionManager();
    }
}
